/*************************************************************************

    calc.c  5.01

*************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "SlkConstants.h"
#include "SlkParse.h"
#include "SlkString.h"
#include "action.h"

extern double  Answer;

// -----------------------------------------------------------------------
// Token peeking, scanning
// -----------------------------------------------------------------------

char    Expression [ 512 ],
       *Scan_text;
double  Attribute = 'X';
double  Last_attribute;

slk_size_t
get_token ( void )
{
    slk_size_t    token;

    while ( isspace ( *Scan_text ) ) {
        ++Scan_text;
    }
    Last_attribute = Attribute;

    if ( ! *Scan_text ) {
        token = END_OF_SLK_INPUT_;
        Attribute = 'Y';
    } else if ( isdigit ( *Scan_text )  ||  *Scan_text == '.' ) {
        token = NUMBER_;
        Attribute = atof ( Scan_text );
        while ( isdigit ( *Scan_text )  ||  *Scan_text == '.' ) {
            ++Scan_text;
        }
    } else {
        token = *Scan_text;
        Attribute = token;
        switch ( token ) {
            case '+' :
                token = PLUS_;
                break;
            case '-' :
                token = MINUS_;
                break;
            case '*' :
                token = STAR_;
                break;
            case '/' :
                token = SLASH_;
                break;
            case '^' :
                token = CARET_;
                break;
            case '(' :
                token = LPAREN_;
                break;
            case ')' :
                token = RPAREN_;
                break;
            case '=' :
                token = EQUAL_; 
                break;
        }   
        ++Scan_text;
    }

    return  token;
}

slk_size_t
peek_token ( int   level )              // scan next token without consuming it
{
    slk_size_t     token = 0;

    printf ( "peek_token is not called in an LL(1) grammar\n" );

    return  token;
}

void
init_peeker ( char    *string )
{
    strncpy ( Expression, string, sizeof ( Expression ) );
    Scan_text = Expression;
}

// -----------------------------------------------------------------------
// parse error routines
// -----------------------------------------------------------------------

#define GET_PRODUCTION_NAME    SlkGetProductionName
#define GET_SYMBOL_NAME        SlkGetSymbolName

slk_size_t
mismatch ( slk_size_t     symbol,
           slk_size_t     token )
{
    printf ( "  expecting '%s' but found '%s' \n", GET_SYMBOL_NAME (symbol),
                                                   GET_SYMBOL_NAME (token) );
    return ( token );
}

slk_size_t
no_entry ( slk_size_t     nonterminal,
           slk_size_t     token,
           int       level )
{
    #ifdef DEBUG
        printf ( "  no entry for nonterminal/token:  %s  %s \n",
                                            GET_SYMBOL_NAME ( nonterminal ),
                                            GET_SYMBOL_NAME (token) );
    #else
        printf ( "  syntax error: skipping input '%s' \n",
                                            GET_SYMBOL_NAME (token) );
    #endif

//  token = get_token ();                       // advance the input LL
//  return ( token );

    get_token ();                       // advance the input LR
    return  0;
}

void
input_left ( void )
{
}

void  message ( char  *message )
{
    fputs ( message, stdout );
}

                 
// -----------------------------------------------------------------------
// main for desk calculator
// -----------------------------------------------------------------------

#define HELP        1
#define DISPLAY     2
#define CONTINUE    4

main ( int     argc,
       char  **argv )
{
    char      *p,
              *expr = NULL;
    int        options = CONTINUE;
    SlkToken   peeker = { get_token, peek_token };
    SlkError   error = { mismatch, no_entry, input_left, message };
    Action_t   action;
    char       scanline [ 512 ];

    while ( --argc > 0 ) {                      // process command line options
        p = *++argv;
        switch ( *p ) {
            case '-':
                while ( *++p ) {
                    switch ( *p ) {
                        case 'd':
                            options |= DISPLAY;
                            break;
                        case 'h':
                        case '?':
                            options |= HELP;
                            break;
                        default:
                            fprintf (stderr, "Invalid option, %c\n", *p);
                    }
                }
                break;
            default:
                expr = p;
        }
    }
    printf ("DESK CALCULATOR: operators are *, /, +, -, ^  (calc -h for help)\n");
    if (options & HELP){
        printf (
            "Usage:  calc  [expression] [ options ]\n\n"
            "  -d    display parse derivation \n"
            "  -h    help \n"
            "\n"
            );
    }
    action = GetAction ( options & DISPLAY );

    if ( expr ) {
        options &= ~CONTINUE;
        init_peeker ( expr );
        SlkParse ( action.slk, peeker, error, 0 );
        printf ("Answer = %f\n", Answer);
        printf ("post order abstract syntax tree traversal:\n" );
        Answer = action.post_order();
        printf ("\nAnswer = %f\n", Answer);

        printf ("=====================\n" );
        printf ("show_parse_derivation\n" );
        printf ("=====================\n" );
        action.show_parse_derivation ();
        printf ("=====================\n" );
        printf ("show_tree\n" );
        printf ("=====================\n" );
        action.show_tree ();
        printf ("=====================\n" );
        printf ("show_expression\n" );
        printf ("=====================\n" );
        action.show_expression ();
        printf ("\nAnswer = %.2f\n", Answer);
    }

    if ( options & CONTINUE ){
        for (;;){
            printf ("Enter expression: ");
            fflush ( stdout );
            p = gets ( scanline );
            if ( p  &&  *p ) {
                init_peeker ( p );
                SlkParse ( action.slk, peeker, error, 0 );
                printf ("Answer = %f\n", Answer);
                printf ("post order abstract syntax tree traversal:\n" );
                Answer = action.post_order();
                printf ("\nAnswer = %f\n", Answer);
            } else {
                break;
            }
	}
    }

    return 0;
}
